package cn.jbit.test.io.nio.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

/**
 * 源码学院只为培养优秀的bat程序员而生
 *
 * @author 学员张在成
 * @data 2020/8/10
 */
public class MultiNioServerHandler implements  Runnable{

    //创建服务器通道对象
    private  static ServerSocketChannel ssc = null;
    //创建一个多路复用器
    private  static Selector selector = null;

    //设置个标记位,采用volatile原因是保证多线程高并发情况下线程对共享变量修改的可见性
    private  volatile  boolean  stop=false;

    public MultiNioServerHandler(int port) {
        try {

            //开启通道
            ssc=ServerSocketChannel.open();
            System.out.println("服务器启动成功");
            //开启多路复用器，检测通道的状态并维护通道的状态。
            selector=Selector.open();
            //绑定端口
            ssc.bind(new InetSocketAddress(8080),1024);//绑定一个等待队列的长度
            //设置它的状态为非阻塞
            ssc.configureBlocking(false);
            //给selector中注册Accept事件
            ssc.register(selector, SelectionKey.OP_ACCEPT);

        }catch (IOException e){
            e.printStackTrace();
            System.exit(1);
        }

    }

    @Override
    public void run() {

        while (!stop){
            //调用select的方法阻塞当前线程,并且设置超时时间
            int select= 0;
            try {
                //通过select获取到我们就绪的事件
                select = selector.select();
                //下面判断的意思是，如果超时时间到了，没有就绪的事件，就继续阻塞着
                if (select==0){
                    continue;
                }

                //将所有得事件放在一个Set集合中
                Set<SelectionKey> selectionKeys=selector.selectedKeys();
                //将set集合交给Iterator遍历
                Iterator<SelectionKey> iterator=selectionKeys.iterator();

                SelectionKey selectionKey=null;

                while (iterator.hasNext()) {
                    //遍历事件
                    selectionKey = iterator.next();
                    //处理事件
                    try {
                        handle(selectionKey);
                    } catch (IOException e) {
                        if (selectionKey!=null){
                            selectionKey.cancel();
                            if (selectionKey.channel()!=null){
                                selectionKey.channel().close();
                            }
                        }
                    }
                    //移除K
                    iterator.remove();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {

            }
        }
    }


    private  void handle(SelectionKey selectionKey) throws IOException {
        //连接事件
        if (selectionKey.isAcceptable()){
            ServerSocketChannel ssc= (ServerSocketChannel) selectionKey.channel();
            SocketChannel socketChannel=ssc.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(selector,SelectionKey.OP_READ);
        }
        //读写事件
        if (selectionKey.isReadable()){
            SocketChannel socketChannel= (SocketChannel) selectionKey.channel();
            ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
            int read=socketChannel.read(byteBuffer);
            if (read>0){
                byteBuffer.flip();
                byte[] bytes=new byte[byteBuffer.remaining()];
                byteBuffer.get(bytes);
                System.out.println("zaicheng  input  data is :"+new String(bytes,"UTF-8"));
                write(socketChannel,"ok");
            }
        }
    }

    //写事件，将服务器的信息响应给客户端
    private  static  void  write(SocketChannel socketChannel,String response) throws IOException {
        if (socketChannel!=null&&response.length()>0){

            //将客户端传来得数据信息转换成字节数组
            byte[] bytes=response.getBytes();
            ByteBuffer  byteBuffer=ByteBuffer.allocate(1024);
            //将客户端发来得请求得字节数组放在缓存区
            byteBuffer.put(bytes);
            //读写模式反转
            byteBuffer.flip();
            //写数据到客户端
            socketChannel.write(byteBuffer);
        }
    }

    public void stop(){
        this.stop=true;
    }
}
